#include <sys/epoll.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h>

#include "tklog.h"
#include "tkconstants.h"
#include "tknetutils.h"
#include "tkevent.h"

#define CUSTOM_SOCKET_TYPE_DEFAULT	0
#define CUSTOM_SOCKET_TYPE_LISTEN	1

struct custom_event {
	void *data;			/* point to custom_socket */

	unsigned int type;
	struct custom_event *next;

	/**
        struct rb_node node;
	unsigned long timeout_time;
	**/
	
	/* kinds of flag */
        unsigned accept_ready:1;
	unsigned readable_flag:1;
        unsigned writable_flag:1;
        unsigned active:1;      	/* register in event pool, active will be 1, and or not */
	unsigned active_read:1;
	unsigned active_write:1;
        unsigned timeout:1;     	/* if the event is timeout set 1 */
        unsigned in_timer_set:1;        /* if the event is set in timer, in_timer_set flag will be 1 */
        unsigned shutdown:1;            /* aready close status, now wait for connection's other side's read or write */
        unsigned error:1;

};

void set_epoll_after_accept(struct epoll_env *env, after_accept_entry after_accept)
{
	env->after_accept = after_accept;
}

void set_epoll_after_connect(struct epoll_env *env, after_connect_entry after_connect)
{
	env->after_connect = after_connect;
}

void* get_custom_event(void *data)
{
	void *p = malloc(sizeof(struct custom_event));
	struct custom_event *event = (struct custom_event*)p;
	memset(event, 0x00, sizeof(struct custom_event));
	
	event->data = data;

	return p;
}

struct custom_socket* get_custom_socket()
{
	struct custom_socket* s = (struct custom_socket*)malloc(sizeof(struct custom_socket));
	if (!s)
		return NULL;

	memset(s, 0x00, sizeof(struct custom_socket));
	s->event = get_custom_event(s);
	if (s->event) {
		free(s);
		return NULL;
	}
	memset(s->event, 0x00, sizeof(struct custom_event));

	return s;	
}

void destory_custom_socket(struct epoll_env *env, struct custom_socket *s)
{
	if (s == NULL)
		return;

	struct custom_event *event = (struct custom_event*)s->event;

	if (s->sockfd > 0) {
		epoll_del_rw_event(env, event);
		close(s->sockfd);
	}
		
	if (s->event) {
		free(s->event);
		s->event = NULL;
	}
	if (s)
		free(s);
}

int is_accept(void *pv)
{
	struct custom_event* event = (struct custom_event*)pv;
	return (event->accept_ready == 1);
} 

int is_writable(void *pv)
{
        struct custom_event* event = (struct custom_event*)pv;
        return (event->writable_flag == 1);	
}

int is_readable(void *pv)
{
	struct custom_event* event = (struct custom_event*)pv;
        return (event->readable_flag == 1);
}

void set_readable(void *pv, int flag)
{
	struct custom_event* event = (struct custom_event*)pv;
	event->readable_flag = flag;
}

void set_writable(void *pv, int flag)
{
	struct custom_event *event = (struct custom_event*)pv;
	event->writable_flag = flag;
}

int is_active(void *pv) 
{
	struct custom_event* event = (struct custom_event*)pv;
        return (event->active == 1);
}

int is_timeout(void *pv) 
{
	struct custom_event* event = (struct custom_event*)pv;
        return (event->timeout == 1);
}

int is_intimerset(void *pv)
{
	struct custom_event* event = (struct custom_event*)pv;
        return (event->in_timer_set == 1);
}

int is_shutdown(void *pv)
{
        struct custom_event* event = (struct custom_event*)pv;
        return (event->shutdown == 1);
}

int is_error(void *pv)
{
        struct custom_event* event = (struct custom_event*)pv;
        return (event->error == 1);
}

void set_event_type(void *pv, int type) 
{
        struct custom_event* event = (struct custom_event*)pv;
	event->type = type;
}

void set_event_data(void *pv, void *data)
{
        struct custom_event* event = (struct custom_event*)pv;
	event->data = data;
}
void* get_event_data(void *pv)
{
        struct custom_event* event = (struct custom_event*)pv;
	return event->data;
}

struct epoll_env* create_epoll_env(after_accept_entry after_accept, after_connect_entry after_connect)
{
	int len = 0;
	int epollfd;
	struct epoll_env *env = NULL;

	env = (struct epoll_env*)malloc(sizeof(struct epoll_env));
	if (!env)
		return NULL;

	env->epollfd = epoll_create(1024);
	
	if (env->epollfd == -1) {
                elog("epoll create failed[%d]-errno[%d]", env->epollfd, errno);
		free(env);	
                return NULL;
	}

        len = EPOLL_MAX_WAITEVENTS_NUM * sizeof(struct epoll_event);
        env->epoll_wait_events = (struct epoll_event*)malloc(len);

        if (env->epoll_wait_events == NULL) {
		free(env);	
                return NULL;
        }
        memset(env->epoll_wait_events, 0x00, len);

	env->after_accept = after_accept;
	env->after_connect = after_connect;

        return env;
	
}

int destory_epoll_env(struct epoll_env *env)
{
	if (env == NULL)
		return 0;

	if (close(env->epollfd) == -1) 
		return -1;
	
	free(env->epoll_wait_events);	
	free(env);
	
	return 0;
}

int epoll_add_event(struct epoll_env *env, void *event, int event_type)
{
	struct epoll_event	ee;
	struct custom_event	*ev = NULL;	/* input event */
	struct custom_socket 	*s = NULL;
	int events = 0;
	int op = 0;
	int needadd = 0;

	ev = (struct custom_event*)event;
	s = (struct custom_socket*)ev->data;


	if (ev->active_write && ev->active_read)
		return 0;

	if (	!ev->active 					
		||
		((event_type & EPOLLIN) && !ev->active_read)
		||
		((event_type & EPOLLOUT) && !ev->active_write)
	) 
	{
		needadd = 1;
	}

	if (!needadd)
		return 0;

	op = EPOLL_CTL_ADD;
	if (event_type & EPOLLIN) {
		if (ev->active_write) {
			events = (events | EPOLLOUT);		
			op = EPOLL_CTL_MOD;
		}
	}	
	else if (event_type & EPOLLOUT) {
		if (ev->active_read) {
			events = (events | EPOLLIN);		
			op = EPOLL_CTL_MOD;
		}
	}

	memset(&ee, 0x00, sizeof(ee));
	ee.events = (events | EPOLLET | EPOLLRDHUP);
	ee.data.ptr = s;

	if (epoll_ctl(env->epollfd, op, s->sockfd, &ee) == -1) {
		elog("epoll ctl failed,op[%d]-events[%d]-errno[%d]", op, ee.events, errno); 
                return -1;
        }

	ev->active = 1;
	if (events & EPOLLIN)
		ev->active_read = 1;
	if (events & EPOLLOUT) 
		ev->active_write = 1;
	
	return 0;
}

int epoll_del_event(struct epoll_env *env, void *event, int event_type)
{
	struct epoll_event	ee;
	struct custom_event	*ev = NULL;	/* input event */
	struct custom_socket	*s = NULL;
	int events = 0;
	int op = 0;

	s = ev->data;
	ev = (struct custom_event*)event;
	if (ev->active == 0) 
		return 0;

	if (ev->active_read && (event_type & EPOLLIN))	
		events |= EPOLLIN;
	if (ev->active_write && (event_type & EPOLLOUT)) 
		events |= EPOLLOUT;

	if (!events)
		return 0;

	op = EPOLL_CTL_DEL;
	if ( 	(events == EPOLLIN && ev->active_write) ||
		(events == EPOLLOUT && ev->active_read) 
	)
	{
		op = EPOLL_CTL_MOD;
	}	

	memset(&ee, 0x00, sizeof(ee));
	ee.events = (events | EPOLLET | EPOLLRDHUP);
	if (op == EPOLL_CTL_DEL)
		ee.events = (ee.events | EPOLLET | EPOLLRDHUP);

	ee.data.ptr = s;
        if (epoll_ctl(env->epollfd, op, s->sockfd, &ee) == -1) {
		elog("epoll ctl failed,op[%d]-events[%d]-errno[%d]", op, ee.events, errno); 
                return -1;
        }
	
	if (events & EPOLLIN)
		ev->active_read = 0;
	if (events & EPOLLOUT)
		ev->active_write = 0;

	if (ev->active_read == 0 && ev->active_write == 0)  {
		ev->active = 0;
	}
	
	return 0;
}

int epoll_add_listen_event(struct epoll_env *env, void *event)
{
	int r = 0;
	struct custom_event	*ev = NULL;	

	r = epoll_add_event(env, event, EPOLLIN);
	if (r) 
		return -1;
	
	/* modify custom_socket's type */
	ev = (struct custom_event*)event;	
	ev->type = CUSTOM_SOCKET_TYPE_LISTEN; 
}

int epoll_del_listen_event(struct epoll_env *env, void *event)
{
	return epoll_del_event(env, event, EPOLLOUT);
}

int epoll_add_read_event(struct epoll_env *env, void *event)
{
	return epoll_add_event(env, event, EPOLLIN);
}

int epoll_add_write_event(struct epoll_env *env, void *event)
{
	return epoll_add_event(env, event, EPOLLOUT);
}

int epoll_del_read_event(struct epoll_env *env, void *event)
{
	return epoll_del_event(env, event, EPOLLIN);
}

int epoll_del_write_event(struct epoll_env *env, void *event)
{
	return epoll_del_event(env, event, EPOLLOUT);
}

int epoll_del_rw_event(struct epoll_env *env, void *event)
{
	return epoll_del_event(env, event, EPOLLIN | EPOLLOUT);
}

int epoll_add_rw_event(struct epoll_env *env, void *event)
{
	return epoll_add_event(env, event, EPOLLIN | EPOLLOUT);
}
int hd_epoll_listen_accept(struct epoll_env *env, struct custom_socket *s, int event_type) 
{
	int r = 0;
	int new_sockfd;
	int new_addrlen;
	struct sockaddr_in new_addr;

	while (1) {
		new_addrlen = sizeof(struct sockaddr_in);
		new_sockfd = accept(s->sockfd, (struct sockaddr*)&new_addr, &new_addrlen);
                if (new_sockfd == -1) {
                        if (errno == EAGAIN || errno == EWOULDBLOCK) {
                                dlog("accept EAGAIN");
                        }
                        else if (errno == EMFILE || errno == ENFILE) {
                                /* need wait for a moment */
                                /* delay accept */
                                dlog("too many fd, access the system limit");
                        }
                        else {
                                elog("listen ip[%s]-port[%d]-listen_fd[%d] accept failed",
								s->addr.ipv4,
								s->addr.port,
								s->sockfd );
                        }
                        break;
                }
		
		if ((s = get_custom_socket()) == NULL) {
			close(new_sockfd);
			elog("not enough memory, force close new sockfd");
			break;
		}

		s->sockfd = new_sockfd;

		/* set tcp opt */
		set_nonblocking(new_sockfd);
		set_reuseaddr(new_sockfd, 1);
		set_tcpkeepalive(new_sockfd);

		/* set address */
		memcpy(&s->addr.sockaddr, &new_addr, new_addrlen); 
		strcpy(s->addr.ipv4, inet_ntoa(new_addr.sin_addr));
		s->addr.port = ntohs(new_addr.sin_port);
		
#if 0
		/* Do by high level, here just new connect socket */
		/* add read event */
		if (epoll_add_read_event(s->event) != 0) {
			destory_custom_socket(s);
			elog("add read event failed");
			break;
		}

		/* set get client infomation handler */
		s->handler = hd_get_client_information;
#endif
		if (env->after_accept(env, s)) {
			elog("after accept custom handler failed");	
		}

		ilog("new client connect,ip[%s]-port[%d]", s->addr.ipv4, s->addr.port);
	}

	return 0;
}

int epoll_process_events(struct epoll_env* env, long timeout)
{
	int r;
	int events_num;
	struct custom_socket *s = NULL;
	struct custom_event *ev = NULL;
	int i;
	int events;

	events_num = epoll_wait(env->epollfd, env->epoll_wait_events, EPOLL_MAX_WAITEVENTS_NUM, timeout);
	r = (events_num == -1) ? errno : 0;
        if (r) {
                if (r == EINTR) {
			elog("epoll wait eintr");
                        return 0;
		}
                else {
			elog("epoll wait failed, errno[%d]", errno);
                        return -1;
		}
        }

	for (i = 0; i < events_num; i++) {
                s = (struct custom_socket*)env->epoll_wait_events[i].data.ptr;
                events = env->epoll_wait_events[i].events;
		ev = s->event;

		ilog("EVENT(ip[%s]-port[%d]-sockedfd[%d]-event[%d])", s->addr.ipv4, s->addr.port, s->sockfd, events);
		
		if (ev->error == 1) {
			elog("EVENT(ip[%s]-port[%d]-sockedfd[%d]-event[%d])", s->addr.ipv4, s->addr.port, s->sockfd, events);
			exit(1);
		}
	
                if (events & (EPOLLERR | EPOLLHUP)) {
			/* local error */
			if (ev->type == CUSTOM_SOCKET_TYPE_LISTEN) {
                                if (!s->handler) 
                                        ilog("listen ip[%s]-port[%d] event's handler is null", s->addr.ipv4, s->addr.port);
                                else	/* directly call s->handler */
                                        s->handler(env, s, EVENT_LOCAL_ERROR);
                        }
                        else {
                                if (!s->handler) 
                                        ilog("ip[%s]-port[%d] event's handler is null", s->addr.ipv4, s->addr.port);
                                else
                                        (VOID)hd_handler_entry(env, s, EVENT_LOCAL_ERROR);
                                elog("client ip[%s] port[%d] occur error when connected", s->addr.ipv4, s->addr.port);
                        } 
			ev->error = 1;
                }
		else if (events & EPOLLRDHUP) {
			if (ev->type == CUSTOM_SOCKET_TYPE_LISTEN) {
				ilog("listen never recive close message, not possible");
				return -1;
			}
			else {
                                if (!s->handler) 
                                        ilog("ip[%s]-port[%d] event's handler is null", s->addr.ipv4, s->addr.port);
				else 
					(VOID)hd_handler_entry(env, s, EVENT_REMOTE_CLOSE);
                                elog("client ip[%s] port[%d] occur error closed", s->addr.ipv4, s->addr.port);
			}
		}

		if ((events & EPOLLIN) || is_readable(s)) {
			if (!s->handler) {
				if (ev->type == CUSTOM_SOCKET_TYPE_LISTEN) {
					flog("listen ip[%s]-port[%d] handler is null", s->addr.ipv4, s->addr.port);
				}
				else {
					flog("socket ip[%s]-port[%d] read handler is null", s->addr.ipv4, s->addr.port);
				}
			}
			else {
                                (VOID)hd_handler_entry(env, s, EVENT_READ);
                        }
                }      
                if (events & EPOLLOUT) {
			/* notice: only has sending data, we should add write event, so writable flag no use */
			if (!s->handler) {
				flog("socket ip[%s]-port[%d] write handler is null", s->addr.ipv4, s->addr.port);
			}
			else {
				(VOID)hd_handler_entry(env, s, EVENT_WRITE);
			}
                }
	
		main_process_clients_list();
	}

	return events_num;
}

void tk_socket_error(struct epoll_env *env, struct custom_socket *s)
{
	destory_custom_socket(env, s);
}
